'use client' import React, { useState, useEffect, useRef } from 'react' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { Button } from '@/components/ui/button' import { Label } from '@/components/ui/label' import { Textarea } from '@/components/ui/textarea' import { Badge } from '@/components/ui/badge' import { Alert, AlertDescription } from '@/components/ui/alert' import { FileText, Send, Save, LoaderIcon, AlertCircle, Eye, ArrowLeft, MessageSquare } from 'lucide-react' import { toast } from 'sonner' import { useRouter } from 'next/navigation' import { getContractForVendorReview, vendorReplyToContractReview, saveVendorCommentDraft } from '@/lib/general-contracts/service' import type { WebViewerInstance } from '@pdftron/webviewer' interface VendorContractReviewClientProps { contract: { id: number contractNumber: string revision: number name: string | null status: string type: string | null category: string | null vendorId: number contractAmount: number | null currency: string | null startDate: string | null endDate: string | null specificationType: string | null specificationManualText: string | null contractScope: string | null notes: string | null contractItems: Array> attachments: Array<{ id: number contractId: number documentName: string fileName: string filePath: string vendorComment: string | null shiComment: string | null uploadedAt: Date uploadedById: number }> vendor: { id: number vendorCode: string | null vendorName: string | null } | null } vendorId: number } export function VendorContractReviewClient({ contract: initialContract, vendorId }: VendorContractReviewClientProps) { const router = useRouter() const [contract, setContract] = useState(initialContract) const [vendorComment, setVendorComment] = useState('') const [isSaving, setIsSaving] = useState(false) const [isSubmitting, setIsSubmitting] = useState(false) // PDFTron Viewer 관련 상태 const viewerRef = useRef(null) const instanceRef = useRef(null) const [viewerLoading, setViewerLoading] = useState(false) const [viewerInitialized, setViewerInitialized] = useState(false) // 계약 첨부파일에서 기존 Vendor Comment 로드 useEffect(() => { if (contract?.attachments && contract.attachments.length > 0) { const firstAttachment = contract.attachments[0] if (firstAttachment.vendorComment) { setVendorComment(firstAttachment.vendorComment) } } }, [contract]) // PDFTron Viewer 초기화 useEffect(() => { if (!viewerRef.current || viewerInitialized) return const initializeViewer = async () => { try { setViewerLoading(true) const { default: WebViewer } = await import('@pdftron/webviewer') if (!viewerRef.current) return const instance = await WebViewer( { path: '/pdftronWeb', licenseKey: process.env.NEXT_PUBLIC_PDFTRON_WEBVIEW_KEY || '', fullAPI: true, enableFilePicker: false, enableMeasurement: false, enableRedaction: false, enableAnnotations: false, enablePrint: false, enableDownload: false, }, viewerRef.current ) instanceRef.current = instance setViewerInitialized(true) setViewerLoading(false) // 계약서 초안 PDF가 있으면 로드 if (contract?.attachments && contract.attachments.length > 0) { const pdfAttachment = contract.attachments.find( (att) => att.filePath && att.filePath.endsWith('.pdf') ) if (pdfAttachment && pdfAttachment.filePath) { // 파일 경로를 완전한 URL로 변환 const fileUrl = pdfAttachment.filePath.startsWith('http') ? pdfAttachment.filePath : `${process.env.NEXT_PUBLIC_URL}${pdfAttachment.filePath}` instance.UI.loadDocument(fileUrl) } } } catch (error) { console.error('PDFTron Viewer 초기화 오류:', error) setViewerLoading(false) toast.error('문서 뷰어를 초기화할 수 없습니다.') } } initializeViewer() return () => { if (instanceRef.current) { try { instanceRef.current.UI.dispose() } catch (error) { console.error('Viewer 정리 오류:', error) } } } }, [contract, viewerInitialized]) // 임시 저장 const handleSaveDraft = async () => { if (!vendorComment.trim()) { toast.error('의견을 입력해주세요.') return } setIsSaving(true) try { await saveVendorCommentDraft(contract.id, vendorComment, vendorId) toast.success('의견이 임시 저장되었습니다.') } catch (error) { console.error('임시 저장 오류:', error) const errorMessage = error instanceof Error ? error.message : '임시 저장에 실패했습니다.' toast.error(errorMessage) } finally { setIsSaving(false) } } // 의견 회신 const handleSubmitReply = async () => { if (!vendorComment.trim()) { toast.error('의견을 입력해주세요.') return } setIsSubmitting(true) try { await vendorReplyToContractReview(contract.id, vendorComment, vendorId) toast.success('의견이 성공적으로 회신되었습니다.') // 계약 정보 다시 로드 const updatedContract = await getContractForVendorReview(contract.id, vendorId) setContract({ ...updatedContract, name: updatedContract.name || '', } as VendorContractReviewClientProps['contract']) // 상태 변경 후 메시지 표시 setTimeout(() => { router.push('/partners/dashboard') }, 2000) } catch (error) { console.error('의견 회신 오류:', error) const errorMessage = error instanceof Error ? error.message : '의견 회신에 실패했습니다.' toast.error(errorMessage) } finally { setIsSubmitting(false) } } const getStatusLabel = (status: string) => { const statusLabels: Record = { 'Draft': '임시저장', 'Request to Review': '조건검토요청', 'Vendor Replied Review': '협력업체 회신', 'SHI Confirmed Review': '당사 검토 확정', 'Contract Accept Request': '계약승인요청', 'Complete the Contract': '계약체결', } return statusLabels[status] || status } const getStatusColor = (status: string) => { const statusColors: Record = { 'Request to Review': 'bg-yellow-100 text-yellow-800', 'Vendor Replied Review': 'bg-blue-100 text-blue-800', 'SHI Confirmed Review': 'bg-green-100 text-green-800', } return statusColors[status] || 'bg-gray-100 text-gray-800' } return (
{/* 헤더 */}

일반계약 조건검토

계약번호: {contract.contractNumber} (Rev.{contract.revision})

{getStatusLabel(contract.status)}
{/* 상태 안내 */} {contract.status === 'Request to Review' && ( 계약 조건 검토를 요청받았습니다. 계약서 초안을 확인하고 의견을 입력해주세요. )} {/* 계약 정보 카드 */} 계약 정보

{contract.name || '-'}

{contract.contractAmount?.toLocaleString() || '0'} {contract.currency || 'KRW'}

{contract.startDate ? new Date(contract.startDate).toLocaleDateString() : '-'} ~{' '} {contract.endDate ? new Date(contract.endDate).toLocaleDateString() : '-'}

{contract.contractScope || '-'}

{/* 계약서 초안 뷰어 */} 계약서 초안
{viewerLoading && (
문서를 불러오는 중...
)}
{/* Vendor Comment 입력 */} {contract.status === 'Request to Review' && ( 검토 의견 입력